1 package org.apache.maven.surefire.junitcore.pc;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.surefire.junitcore.JUnitCoreParameters;
23 import org.apache.maven.surefire.testset.TestSetFailedException;
24
25
26
27
28
29
30
31
32
33
34 final class ParallelComputerUtil
35 {
36 private static int availableProcessors = Runtime.getRuntime().availableProcessors();
37
38 private ParallelComputerUtil()
39 {
40 throw new IllegalStateException( "Suppresses calling constructor, ensuring non-instantiability." );
41 }
42
43
44
45
46 static void overrideAvailableProcessors( int availableProcessors )
47 {
48 ParallelComputerUtil.availableProcessors = availableProcessors;
49 }
50
51
52
53
54 static void setDefaultAvailableProcessors()
55 {
56 ParallelComputerUtil.availableProcessors = Runtime.getRuntime().availableProcessors();
57 }
58
59 static Concurrency resolveConcurrency( JUnitCoreParameters params, RunnerCounter counts )
60 throws TestSetFailedException
61 {
62 if ( !params.isParallelismSelected() )
63 {
64 throw new TestSetFailedException( "Unspecified parameter '" + JUnitCoreParameters.PARALLEL_KEY + "'." );
65 }
66
67 if ( !params.isUseUnlimitedThreads() && !hasThreadCount( params ) && !hasThreadCounts( params ) )
68 {
69 throw new TestSetFailedException( "Unspecified thread-count(s). " +
70 "See the parameters " + JUnitCoreParameters.USEUNLIMITEDTHREADS_KEY
71 + ", " + JUnitCoreParameters.THREADCOUNT_KEY + ", "
72 + JUnitCoreParameters.THREADCOUNTSUITES_KEY + ", "
73 + JUnitCoreParameters.THREADCOUNTCLASSES_KEY + ", "
74 + JUnitCoreParameters.THREADCOUNTMETHODS_KEY + "." );
75 }
76
77 if ( params.isUseUnlimitedThreads() )
78 {
79 return concurrencyForUnlimitedThreads( params );
80 }
81 else if ( hasThreadCount( params ) )
82 {
83 if ( hasThreadCounts( params ) )
84 {
85 return isLeafUnspecified( params )
86 ? concurrencyFromAllThreadCountsButUnspecifiedLeafCount( params, counts )
87 : concurrencyFromAllThreadCounts( params );
88 }
89 else
90 {
91 return estimateConcurrency( params, counts );
92 }
93 }
94 else
95 {
96 return concurrencyFromThreadCounts( params );
97 }
98 }
99
100 private static Concurrency concurrencyForUnlimitedThreads( JUnitCoreParameters params )
101 {
102 Concurrency concurrency = new Concurrency();
103 concurrency.suites = params.isParallelSuites() ? threadCountSuites( params ) : 0;
104 concurrency.classes = params.isParallelClasses() ? threadCountClasses( params ) : 0;
105 concurrency.methods = params.isParallelMethods() ? threadCountMethods( params ) : 0;
106 concurrency.capacity = Integer.MAX_VALUE;
107 return concurrency;
108 }
109
110 private static Concurrency estimateConcurrency( JUnitCoreParameters params, RunnerCounter counts )
111 {
112 final Concurrency concurrency = new Concurrency();
113 final int parallelEntities = countParallelEntities( params );
114 concurrency.capacity = multiplyByCoreCount( params, params.getThreadCount() );
115 if ( parallelEntities == 1 || counts == null || counts.classes == 0 )
116 {
117
118 double ratio = 1d / parallelEntities;
119 int threads = multiplyByCoreCount( params, ratio * params.getThreadCount() );
120 concurrency.suites = params.isParallelSuites() ? minSuites( threads, counts ) : 0;
121 concurrency.classes = params.isParallelClasses() ? minClasses( threads, counts ) : 0;
122 concurrency.methods = params.isParallelMethods() ? minMethods( threads, counts ) : 0;
123 if ( parallelEntities == 1 )
124 {
125 concurrency.capacity = 0;
126 }
127 else
128 {
129 adjustLeaf( params, concurrency );
130 }
131 }
132 else
133 {
134
135 concurrency.suites = params.isParallelSuites() ? toNonNegative( counts.suites ) : 0;
136 concurrency.classes = params.isParallelClasses() ? toNonNegative( counts.classes ) : 0;
137 concurrency.methods =
138 params.isParallelMethods() ? toNonNegative( Math.ceil( counts.methods / (double) counts.classes ) ) : 0;
139 double sum = toNonNegative( concurrency.suites + concurrency.classes + concurrency.methods );
140 if ( concurrency.capacity < sum && sum != 0 )
141 {
142
143 double weight = concurrency.capacity / sum;
144 concurrency.suites *= weight;
145 concurrency.classes *= weight;
146 concurrency.methods *= weight;
147 }
148 adjustLeaf( params, concurrency );
149 }
150 return concurrency;
151 }
152
153 private static Concurrency concurrencyFromAllThreadCountsButUnspecifiedLeafCount( JUnitCoreParameters params,
154 RunnerCounter counts )
155 {
156 Concurrency concurrency = new Concurrency();
157 concurrency.suites = params.isParallelSuites() ? params.getThreadCountSuites() : 0;
158 concurrency.suites = params.isParallelSuites() ? multiplyByCoreCount( params, concurrency.suites ) : 0;
159 concurrency.classes = params.isParallelClasses() ? params.getThreadCountClasses() : 0;
160 concurrency.classes = params.isParallelClasses() ? multiplyByCoreCount( params, concurrency.classes ) : 0;
161 concurrency.methods = params.isParallelMethods() ? params.getThreadCountMethods() : 0;
162 concurrency.methods = params.isParallelMethods() ? multiplyByCoreCount( params, concurrency.methods ) : 0;
163 concurrency.capacity = multiplyByCoreCount( params, params.getThreadCount() );
164
165 if ( counts != null )
166 {
167 concurrency.suites = toNonNegative( Math.min( concurrency.suites, counts.suites ) );
168 concurrency.classes = toNonNegative( Math.min( concurrency.classes, counts.classes ) );
169 }
170
171 setLeafInfinite( params, concurrency );
172
173 return concurrency;
174 }
175
176 private static Concurrency concurrencyFromAllThreadCounts( JUnitCoreParameters params )
177 {
178 Concurrency concurrency = new Concurrency();
179 concurrency.suites = params.isParallelSuites() ? params.getThreadCountSuites() : 0;
180 concurrency.classes = params.isParallelClasses() ? params.getThreadCountClasses() : 0;
181 concurrency.methods = params.isParallelMethods() ? params.getThreadCountMethods() : 0;
182 concurrency.capacity = params.getThreadCount();
183 double all = sumThreadCounts( concurrency );
184
185 concurrency.suites = params.isParallelSuites() ? multiplyByCoreCount( params, concurrency.capacity * (
186 concurrency.suites / all ) ) : 0;
187
188 concurrency.classes = params.isParallelClasses() ? multiplyByCoreCount( params, concurrency.capacity * (
189 concurrency.classes / all ) ) : 0;
190
191 concurrency.methods = params.isParallelMethods() ? multiplyByCoreCount( params, concurrency.capacity * (
192 concurrency.methods / all ) ) : 0;
193
194 concurrency.capacity = multiplyByCoreCount( params, concurrency.capacity );
195 adjustPrecisionInLeaf( params, concurrency );
196 return concurrency;
197 }
198
199 private static Concurrency concurrencyFromThreadCounts( JUnitCoreParameters params )
200 {
201 Concurrency concurrency = new Concurrency();
202 concurrency.suites = params.isParallelSuites() ? threadCountSuites( params ) : 0;
203 concurrency.classes = params.isParallelClasses() ? threadCountClasses( params ) : 0;
204 concurrency.methods = params.isParallelMethods() ? threadCountMethods( params ) : 0;
205 concurrency.capacity = toNonNegative( sumThreadCounts( concurrency ) );
206 return concurrency;
207 }
208
209 private static int countParallelEntities( JUnitCoreParameters params )
210 {
211 int count = 0;
212 if ( params.isParallelSuites() )
213 {
214 count++;
215 }
216
217 if ( params.isParallelClasses() )
218 {
219 count++;
220 }
221
222 if ( params.isParallelMethods() )
223 {
224 count++;
225 }
226 return count;
227 }
228
229 private static void adjustPrecisionInLeaf( JUnitCoreParameters params, Concurrency concurrency )
230 {
231 if ( params.isParallelMethods() )
232 {
233 concurrency.methods = concurrency.capacity - concurrency.suites - concurrency.classes;
234 }
235 else if ( params.isParallelClasses() )
236 {
237 concurrency.classes = concurrency.capacity - concurrency.suites;
238 }
239 }
240
241 private static void adjustLeaf( JUnitCoreParameters params, Concurrency concurrency )
242 {
243 if ( params.isParallelMethods() )
244 {
245 concurrency.methods = Integer.MAX_VALUE;
246 }
247 else if ( params.isParallelClasses() )
248 {
249 concurrency.classes = Integer.MAX_VALUE;
250 }
251 }
252
253 private static void setLeafInfinite( JUnitCoreParameters params, Concurrency concurrency )
254 {
255 if ( params.isParallelMethods() )
256 {
257 concurrency.methods = Integer.MAX_VALUE;
258 }
259 else if ( params.isParallelClasses() )
260 {
261 concurrency.classes = Integer.MAX_VALUE;
262 }
263 else if ( params.isParallelSuites() )
264 {
265 concurrency.suites = Integer.MAX_VALUE;
266 }
267 }
268
269 private static boolean isLeafUnspecified( JUnitCoreParameters params )
270 {
271 int maskOfParallel = params.isParallelSuites() ? 4 : 0;
272 maskOfParallel |= params.isParallelClasses() ? 2 : 0;
273 maskOfParallel |= params.isParallelMethods() ? 1 : 0;
274
275 int maskOfConcurrency = params.getThreadCountSuites() > 0 ? 4 : 0;
276 maskOfConcurrency |= params.getThreadCountClasses() > 0 ? 2 : 0;
277 maskOfConcurrency |= params.getThreadCountMethods() > 0 ? 1 : 0;
278
279 maskOfConcurrency &= maskOfParallel;
280
281 int leaf = Integer.lowestOneBit( maskOfParallel );
282 return maskOfConcurrency == maskOfParallel - leaf;
283 }
284
285 private static double sumThreadCounts( Concurrency concurrency )
286 {
287 double sum = concurrency.suites;
288 sum += concurrency.classes;
289 sum += concurrency.methods;
290 return sum;
291 }
292
293 private static boolean hasThreadCounts( JUnitCoreParameters jUnitCoreParameters )
294 {
295 return jUnitCoreParameters.isParallelSuites() && jUnitCoreParameters.getThreadCountSuites() > 0 ||
296 jUnitCoreParameters.isParallelClasses() && jUnitCoreParameters.getThreadCountClasses() > 0 ||
297 jUnitCoreParameters.isParallelMethods() && jUnitCoreParameters.getThreadCountMethods() > 0;
298 }
299
300 private static boolean hasThreadCount( JUnitCoreParameters jUnitCoreParameters )
301 {
302 return jUnitCoreParameters.getThreadCount() > 0;
303 }
304
305 private static int threadCountMethods( JUnitCoreParameters jUnitCoreParameters )
306 {
307 return multiplyByCoreCount( jUnitCoreParameters, jUnitCoreParameters.getThreadCountMethods() );
308 }
309
310 private static int threadCountClasses( JUnitCoreParameters jUnitCoreParameters )
311 {
312 return multiplyByCoreCount( jUnitCoreParameters, jUnitCoreParameters.getThreadCountClasses() );
313 }
314
315 private static int threadCountSuites( JUnitCoreParameters jUnitCoreParameters )
316 {
317 return multiplyByCoreCount( jUnitCoreParameters, jUnitCoreParameters.getThreadCountSuites() );
318 }
319
320 private static int multiplyByCoreCount( JUnitCoreParameters jUnitCoreParameters, double threadsPerCore )
321 {
322 double numberOfThreads =
323 jUnitCoreParameters.isPerCoreThreadCount() ? threadsPerCore * (double) availableProcessors : threadsPerCore;
324
325 return numberOfThreads > 0 ? toNonNegative( numberOfThreads ) : Integer.MAX_VALUE;
326 }
327
328 private static int minSuites( int threads, RunnerCounter counts )
329 {
330 long count = counts == null ? Integer.MAX_VALUE : counts.suites;
331 return Math.min( threads, toNonNegative( count ) );
332 }
333
334 private static int minClasses( int threads, RunnerCounter counts )
335 {
336 long count = counts == null ? Integer.MAX_VALUE : counts.classes;
337 return Math.min( threads, toNonNegative( count ) );
338 }
339
340 private static int minMethods( int threads, RunnerCounter counts )
341 {
342 long count = counts == null ? Integer.MAX_VALUE : counts.methods;
343 return Math.min( threads, toNonNegative( count ) );
344 }
345
346 private static int toNonNegative( long num )
347 {
348 return (int) Math.min( num > 0 ? num : 0, Integer.MAX_VALUE );
349 }
350
351 private static int toNonNegative( double num )
352 {
353 return (int) Math.min( num > 0 ? num : 0, Integer.MAX_VALUE );
354 }
355 }